﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace DownloadTS
{
    public partial class Form1 : Form
    {
        delegate void SetTextCallback(string text);
        private object locker = new object();
        public Form1()
        {
            InitializeComponent();
        }

        private static int totalNum = 0;
        private static int remainNum = 0;
        private static int successNum = 0;
        private static int failNum = 0;

        private int startNum = 0;
        private int endNum = 0;
        private string url;
        private string savePath;
        private int group = 1;
        private HttpDldFile downloader = new HttpDldFile();

        private static List<string> failList = new List<string>();

        private void button1_Click(object sender, EventArgs e)
        {
            FolderBrowserDialog dialog = new FolderBrowserDialog();
            dialog.Description = "请选择保存路径";
            if (dialog.ShowDialog() == DialogResult.OK)
            {
                if (string.IsNullOrEmpty(dialog.SelectedPath))
                {
                    MessageBox.Show(this, "文件夹路径不能为空", "提示");
                    return;
                }
                txt_savePath.Text = dialog.SelectedPath;
            }
        }

        public string fillNum(int i)
        {
            string result = "" + i;
            int len = Int32.Parse(txt_numLen.Text);
            if (result.Length < len)
            {
                int dis = len - result.Length;
                for (int j = 0; j < dis; j++)
                {
                    result = "0" + result;
                }
            }
            return result;
        }

        private void button2_Click(object sender, EventArgs e)
        {
            tb_output.Text = "";
            startNum = Int32.Parse(txt_startNum.Text);
            endNum = Int32.Parse(txt_endNum.Text);
            int filllen = Int32.Parse(txt_numLen.Text);
            this.url = txt_url.Text;
            this.savePath = txt_savePath.Text;
            int threadNum = Int32.Parse(txt_threadNum.Text);
            this.url = url.Substring(0, url.Length - 3- filllen);
            output("下載地址：" + url);
            totalNum = endNum - startNum + 1;
            remainNum = totalNum;
            successNum = 0;
            failNum = 0;
            lab_total.Text = totalNum + "";
            lab_remain.Text = remainNum + "";
            lab_success.Text = successNum + "";
            lab_fail.Text = failNum + "0";
            group = totalNum / threadNum;
            if (totalNum % threadNum > 0)
            {
                group++;
            }

            DirectoryInfo df = new DirectoryInfo(savePath);
            if (!df.Exists)
            {
                df.Create();
            }

            output("开始下载");
            for (int i = 0; i < threadNum; i++)
            {
                Thread thread = new Thread(new ParameterizedThreadStart(downgroup));
                thread.Start(startNum + (i * group));
            }

            //for (int i = startNum; i <= endNum; i++)
            //{
            //    downfile(i);
            //}
        }

        private void downgroup(object obj)
        {
            int groupStart = (int)obj;
            if (groupStart < 0)
            {
                return;
            }

            for (int i = groupStart; i < groupStart + group; i++)
            {
                downfile(i);
            }
        }

        private void downfile(object obj)
        {
            int i = (int)obj;
            if (i < startNum || i > endNum)
            {
                return;
            }
            string name = fillNum(i) + ".ts";
            string downUrl = url + name;
            string saveFile = "";
            if (!savePath.EndsWith("/"))
            {
                saveFile = savePath + "/" + name;
            }
            else
            {
                saveFile = savePath + name;
            }
            bool isDown = downloader.Download(downUrl, saveFile);
            lock (locker)
            {
                remainNum--;
                if (isDown)
                {
                    successNum++;
                    output("第" + i + "个文件(" + downUrl + ")下载完成");
                }
                else
                {
                    failNum++;
                    failList.Add(downUrl + "," + i);
                    output("第" + i + "个文件(" + downUrl + ")下载失败");
                }
                if (remainNum == 0 && failList.Count > 0)
                {
                    output("==============================");
                    foreach (string fail in failList)
                    {
                        output(fail);
                    }
                }
            }

        }

        private void output(string txt)
        {
            if (this.tb_output.InvokeRequired)
            {
                while (!this.tb_output.IsHandleCreated)
                {
                    //解决窗体关闭时出现“访问已释放句柄“的异常
                    if (this.tb_output.Disposing || this.tb_output.IsDisposed)
                        return;
                }
                SetTextCallback d = new SetTextCallback(output);
                this.tb_output.Invoke(d, new object[] { txt });
            }
            else
            {
                tb_output.AppendText(txt + System.Environment.NewLine);
                lab_remain.Text = remainNum + "";
                lab_success.Text = successNum + "";
                lab_fail.Text = failNum + "";
            }
        }

        private void btn_mergeFiles_Click(object sender, EventArgs e)
        {
            if (savePath == null || "" == savePath)
            {
                savePath = txt_savePath.Text;
            }
            Thread thread = new Thread(mergeFiles);
            thread.Start();
        }

        private void mergeFiles()
        {
            output("开始合并文件");
            DirectoryInfo root = new DirectoryInfo(savePath);
            FileInfo[] files = root.GetFiles();
            List<CompareFile> fileList = new List<CompareFile>();
            foreach (FileInfo file in files)
            {
                fileList.Add(new CompareFile(file));
            }
            fileList.Sort();
            string newFile = savePath;
            if (!savePath.EndsWith("/"))
            {
                newFile += "/";
            }
            newFile = newFile + GetTimestamp() + ".ts";
            FileStream writer = new FileStream(newFile, FileMode.OpenOrCreate);
            int index = 0;
            foreach (CompareFile file in fileList)
            {
                FileStream reader = file.File.OpenRead();
                writeFile(reader, writer);
                reader.Close();
                index++;
                output("第" + index + "个文件合并完成");
            }
            writer.Close();
            output(newFile + "合并完成");
        }

        private void writeFile(FileStream reader, FileStream writer)
        {
            byte[] writeBytes = new byte[4096];
            int offset = 0;
            int readNum = reader.Read(writeBytes, offset, writeBytes.Length);
            while (readNum > 0)
            {
                writer.Write(writeBytes, offset, readNum);
                readNum = reader.Read(writeBytes, offset, writeBytes.Length);
            }
        }

        public static long GetTimestamp()
        {
            TimeSpan ts = DateTime.Now.ToUniversalTime() - new DateTime(1970, 1, 1);//ToUniversalTime()转换为标准时区的时间,去掉的话直接就用北京时间
            return (long)ts.TotalMilliseconds; //精确到毫秒
            //return (long)ts.TotalSeconds;//获取10位
        }

        private void Form1_FormClosed(object sender, FormClosedEventArgs e)
        {
            Environment.Exit(0);
        }

        private void button3_Click(object sender, EventArgs e)
        {
            if (failList != null && failList.Count > 0)
            {
                totalNum = failList.Count;
                remainNum = totalNum;
                successNum = 0;
                failNum = 0;
                lab_total.Text = totalNum + "";
                lab_remain.Text = remainNum + "";
                lab_success.Text = successNum + "";
                lab_fail.Text = failNum + "0";
                output("==========重新下载失败文件============");
                foreach (string failFile in failList)
                {
                    string[] temp = failFile.Split(',');
                    if (temp != null && temp.Length > 1)
                    {
                        int num = Int32.Parse(temp[1]);
                        Thread thread = new Thread(new ParameterizedThreadStart(downfile));
                        thread.Start(startNum + (num));
                    }
                }
                failList.Clear();
            }
            else
            {
                output("已经全部下载完成");
            }
        }

        private void btn_downloadInfo_Click(object sender, EventArgs e)
        {
            output("=====>总数:" + totalNum);
            output("=====>剩余:" + remainNum);
            output("=====>成功:" + successNum);
            output("=====>每组大小:" + group);
            output("=====>失败:" + failNum);
            output("=====>失败列表数:" + failList.Count);
        }

        public static string Escape(string str)
        {
            StringBuilder sb = new StringBuilder();
            foreach (char c in str)
            {
                sb.Append((Char.IsLetterOrDigit(c)
                || c == '-' || c == '_' || c == '\\'
                || c == '/' || c == '.') ? c.ToString() : Uri.HexEscape(c));
            }
            return sb.ToString();
        }

        public static string UnEscape(string str)
        {
            StringBuilder sb = new StringBuilder();
            int len = str.Length;
            int i = 0;
            while (i != len)
            {
                if (Uri.IsHexEncoding(str, i))
                    sb.Append(Uri.HexUnescape(str, ref i));
                else
                    sb.Append(str[i++]);
            }
            return sb.ToString();
        }

    }
}
